﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CustomProvider.Event;
using System.ComponentModel;
using System.Threading;
using CustomProvider;
using ClientSide.Communication;
using ClientSide.Providers;

namespace ClientSide
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Fields

        /// <summary>
        /// Action for interacting with the configuration form
        /// </summary>
        public Action ActionCloseForm;

        /// <summary>
        /// The object to store parameters for a question
        /// </summary>
        public NotifyStartGameEvent.GameInfoParam GameInfoParam = null;

        /// <summary>
        /// Load question in separate thread
        /// </summary>
        private BackgroundWorker backroungWorker;

        /// <summary>
        /// The socket for communication with the Server
        /// </summary>
        public AsynSocket AsynSocket = new AsynSocket();

        /// <summary>
        /// ID of Player
        /// </summary>
        public string IDPlayer = string.Empty;

        /// <summary>
        /// The permission to turn
        /// </summary>
        private bool IsAllowForGuess = false;

        /// <summary>
        /// The answer of the question
        /// </summary>
        private string AnswerSecret = string.Empty;

        /// <summary>
        /// Defines contanst for this class
        /// </summary>
        public static class Const
        {
            /// <summary>
            /// The message GUESS_MSG which is sent to the server
            /// </summary>
            public const string GUESS_MSG = "GUESS";

            /// <summary>
            /// The message ENDGAME which is sent to the server
            /// </summary>
            public const string ENDGAME_MSG = "ENDGAME";

            /// <summary>
            /// The message PLAYAGAIN which is sent to the server
            /// </summary>
            public const string PLAYAGAIN_MSG = "PLAYAGAIN";
        }
        
        #endregion

        #region Constructors

        /// <summary>
        /// The default constructor
        /// </summary>
        public MainWindow()
        {
            this.InitializeComponent();
            this.SubscribeNotifyAnswerResultEvent();
            this.SubscribeNotifyGameFinishedEvent();
            this.SubscribeNotifyOpponentDisconnectedEvent();
            this.SubscribeStartGame();
            txtMessageInGame.Text = "Waiting question from the server";
        }

        #endregion

        #region Methods

        /// <summary>
        /// Display message on user interface
        /// </summary>
        private void SubscribeStartGame()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyStartGameEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    this.GameInfoParam = EventData;
                    this.LoadQuestion();
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Display message on user interface
        /// </summary>
        private void SubscribeNotifyOpponentDisconnectedEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyOpponentDisconnectedEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    var idPlayer = EventData;
                    var msg = string.Format("The player {0} at IDAddress {1} has been disconnected",
                        CommonMethodProvider.GetPlayerNameFromIDPlayer(idPlayer), CommonMethodProvider.GetIPAddressFromIDPlayer(idPlayer));
                    this.txtMessageInGame.Text = msg;

                    MessageBox.Show(
                        string.Format("The player {0} at IDAddress {1} has been disconnected, you will be disconnected too",
                        CommonMethodProvider.GetPlayerNameFromIDPlayer(idPlayer), CommonMethodProvider.GetIPAddressFromIDPlayer(idPlayer)),
                        "Information", MessageBoxButton.OK);

                    this.Close();
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Received message GameFinished from the server
        /// </summary>
        private void SubscribeNotifyGameFinishedEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyGameFinishedEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    var idWinner = EventData;
                    
                    // Update message in game
                    var msg = string.Format("The player {0} at IDAddress {1} is the winner",
                        CommonMethodProvider.GetPlayerNameFromIDPlayer(idWinner), CommonMethodProvider.GetIPAddressFromIDPlayer(idWinner));
                    this.txtMessageInGame.Text = msg;

                    // Update Points
                    if (this.IDPlayer.Equals(idWinner))
                        this.txtPoint1.Text = (int.Parse(txtPoint1.Text) + 1).ToString();
                    else
                        this.txtPoint2.Text = (int.Parse(txtPoint2.Text) + 1).ToString();

                    if (MessageBox.Show(string.Format("The player {0} at IDAddress {1} is the winner, do you want to play again",
                        CommonMethodProvider.GetPlayerNameFromIDPlayer(idWinner), CommonMethodProvider.GetIPAddressFromIDPlayer(idWinner)), 
                        "Information", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
                        this.PlayAgain();
                    else
                        this.Close();
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Receive result of the answer from the Server
        /// </summary>
        private void SubscribeNotifyAnswerResultEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyAnswerResultEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    // The answer is true, update the answer secret
                    if (EventData.Result)
                    {
                        var positions = EventData.Positions.Split(new char[] { ',' });

                        foreach (string pos in positions)
                        {
                            this.AnswerSecret = this.AnswerSecret.ReplaceCharWithChar(int.Parse(pos), char.Parse(EventData.Character));
                            var txt = this.GetTemplateChildByName(gridWord, "txtAnswer" + pos) as TextBlock;
                            var border = txt.Parent as Border;
                            txt.Text = EventData.Character;
                            border.Background = Brushes.Blue;
                        }

                        // Update message in game for clients
                        txtMessageInGame.Text = string.Format("There's {0} letter(s) in the phrase, next for player {1}",
                            positions.Length, CommonMethodProvider.GetPlayerNameFromIDPlayer(EventData.IDPlayer));
                    }
                    else
                    {
                        // The answer is false, inform the messge to the Clients
                        var otherPlayer = this.GameInfoParam.IDPlayer1.Equals(EventData.IDPlayer) ? this.GameInfoParam.IDPlayer2 : this.GameInfoParam.IDPlayer1;
                        txtMessageInGame.Text = string.Format("There's no letter {0} in the phrase, the player {1} is allow for guess",
                            EventData.Character, CommonMethodProvider.GetPlayerNameFromIDPlayer(otherPlayer));
                    }

                    // The current player that keeps the permission for guess
                    if (this.IDPlayer.Equals(EventData.IDPlayer))
                    {
                        // Continue to guess
                        if (EventData.Result)
                        {
                            // Checking victory
                            if (this.AnswerSecret.Contains("*"))
                                this.IsAllowForGuess = true;
                            else
                            {
                                this.IsAllowForGuess = false;
                                // Send End Game to the Server
                                var msg = string.Format("{0}:{1}", Const.ENDGAME_MSG, this.IDPlayer);
                                this.AsynSocket.SendSync(msg);
                            }
                        }
                    }
                    else
                    {
                        // Change the permission for guess to another player
                        if (EventData.Result == false)
                            this.IsAllowForGuess = true;
                    }
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Play game again
        /// </summary>
        /// <param name="resetPoints"></param>
        private void PlayAgain()
        {
            this.IsAllowForGuess = false;
            this.AnswerSecret = string.Empty;
            this.txtSubject.Text = "Waiting Subject..";
            this.txtSuggestion.Text = "Waiting Suggestion...";
            this.txtMessageInGame.Text = "Waiting question from the server";
            this.gridWord.Children.Clear();

            var msg = string.Format("{0}:{1}#{2}#{3}",
                Const.PLAYAGAIN_MSG, this.IDPlayer, this.GameInfoParam.Subject, this.GameInfoParam.Suggestion);
            this.AsynSocket.SendSync(msg);
        }

        /// <summary>
        /// Find first child control with specific name.
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        private FrameworkElement GetTemplateChildByName(DependencyObject parent, string name)
        {
            int childnum = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childnum; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                if (child is FrameworkElement && ((FrameworkElement)child).Name == name)
                    return child as FrameworkElement;
                else
                {
                    var s = GetTemplateChildByName(child, name);
                    if (s != null)
                        return s;
                }
            }
            return null;
        }

        /// <summary>
        /// Handle closing form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Closed(object sender, EventArgs e)
        {
            // Invoke action for interacting with the Configuration form
            this.ActionCloseForm();
            // Close the socket
            this.AsynSocket.Disconnect(this.IDPlayer);
        }

        /// <summary>
        /// Handle loading form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.LoadQuestion();
        }

        /// <summary>
        /// Load question information
        /// </summary>
        private void LoadQuestion()
        {
            backroungWorker = new BackgroundWorker();
            backroungWorker.DoWork += new DoWorkEventHandler(backroungWorker_DoWork);
            backroungWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backroungWorker_RunWorkerCompleted);
            backroungWorker.RunWorkerAsync();
        }

        /// <summary>
        /// Handle worker executing
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backroungWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // Sleep thread worker
            Thread.Sleep(1000);

            // Execute update question information
            Action action = () =>
            {
                var idPlayer1 = this.IDPlayer;
                var idPlayer2 = idPlayer1.Equals(this.GameInfoParam.IDPlayer1) ?
                                this.GameInfoParam.IDPlayer2 : this.GameInfoParam.IDPlayer1;
                this.IsAllowForGuess = this.IDPlayer.Equals(this.GameInfoParam.IDPlayerAllowGuess) ? true : false;

                txtPlayer1.Text = CommonMethodProvider.GetPlayerNameFromIDPlayer(idPlayer1);
                txtPlayer2.Text = CommonMethodProvider.GetPlayerNameFromIDPlayer(idPlayer2);
                txtSubject.Text = this.GameInfoParam.Subject;
                txtSuggestion.Text = this.GameInfoParam.Suggestion;
                txtMessageInGame.Text = "Received question from the server";

                for (int i = 0; i < this.GameInfoParam.NumCharsOfAnswer; i++)
                {
                    AnswerSecret += "*";
                    var border = new Border
                    {
                        BorderBrush = Brushes.White,
                        Background = Brushes.Red,
                        BorderThickness = new Thickness(2),
                        CornerRadius = new CornerRadius(6),
                        Margin = new Thickness(5, 10, 5, 10)
                    };
                    border.SetValue(Grid.ColumnProperty, i);
                    gridWord.Children.Add(border);

                    var textBlock = new TextBlock
                    {
                        VerticalAlignment = System.Windows.VerticalAlignment.Center,
                        HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
                        Style = this.Resources["wordStyle"] as Style,
                        Name = "txtAnswer" + i.ToString()
                    };
                    border.Child = textBlock;
                }
            };
            this.Dispatcher.BeginInvoke(action);

            // Sleep thread worker to display message in GUI
            Thread.Sleep(1000);
        }

        /// <summary>
        /// Handle worker execute completely
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backroungWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Action action = () =>
            {
                txtMessageInGame.Text = string.Format("The player {0} at IPAddress {1} is allow for turning.",
                                        CommonMethodProvider.GetPlayerNameFromIDPlayer(this.GameInfoParam.IDPlayerAllowGuess),
                                        CommonMethodProvider.GetIPAddressFromIDPlayer(this.GameInfoParam.IDPlayerAllowGuess));
            };
            this.Dispatcher.BeginInvoke(action);
        }

        /// <summary>
        /// Handle click button base on bubbling event on Grid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Grid_Click(object sender, RoutedEventArgs e)
        {
            if (this.IsAllowForGuess == false)
                return;
            // Update permission to prevent user click more
            this.IsAllowForGuess = false;
            // Send guess to the server
            var source = e.OriginalSource as Button;
            var msg = string.Format("{0}:{1}#{2}#{3}#{4}",
                Const.GUESS_MSG, this.IDPlayer, source.Content.ToString(), this.GameInfoParam.Subject, this.GameInfoParam.Suggestion);
            this.AsynSocket.SendSync(msg);

            // PLay click sound
            SoundProvider.Instance.PlaySound(@"Resources/click_09.wav");
        }

        #endregion
    }
}
